"""
@File    :  my_dhcp_client.py
@Author  :  LeeSen
@Date    :  2024-08-05
@Version :  Python 3.7.4
"""
from enum import Enum
from random import randint


class ArpPackageType(Enum):
    REQUEST = 1
    REPLY = 2


class DhcpState(Enum):
    INIT = 0
    SELECTING = 1
    REQUESTING = 2
    BOUND = 3
    RENEWING = 4
    REBINDING = 5
    WAITING = 6


class DhcpEvent(Enum):
    NULL = 0
    DISCOVER_SEND = 1
    OFFER_RECEIVED = 2
    ACK = 3
    NACK = 4
    TIMEOUT = 5
    ERROR = 6
    ARP_RECEIVED = 7


class TimeOutType(Enum):
    DEFAULT = 0
    TIME_1 = 1
    TIME_2 = 2
    TIME_3 = 3
    EXPIRED = 4


class DhcpPackageType(Enum):
    DISCOVER = 1
    OFFER = 2
    REQUEST = 3
    DECLINE = 4
    ACK = 5
    NACK = 6
    RELEASE = 7
    INFORM = 8
    FORCE_RENEW = 9
    LEASE_QUERY = 10
    LEASE_UNASSIGNED = 11
    LEASE_UNKNOWN = 12
    LEASE_ACTIVE = 13


def int_to_mac_suffix(value):
    step = 2
    hex_string = '{:010x}'.format(value)
    return ":".join([str(hex_string[i:i + step]) for i in range(0, len(hex_string), 2)])


class DhcpMessageDTO:
    def __init__(self, host_mac, pkt, dhcp_event):
        self.host_mac = host_mac
        self.pkt = pkt
        self.current_event = dhcp_event


class DhcpOptionsDTO:
    def __init__(self):
        self.__subnet_mask = None
        self.__router = None
        self.__domain = None
        self.__name_server = None
        self.__hostname = None
        self.__server_mac = None
        self.__server_ip = None
        self.__message_type = None
        self.__transaction_id = None
        self.__lease_time = None
        self.__client_ip = None

    @property
    def client_ip(self):
        return self.__client_ip

    @client_ip.setter
    def client_ip(self, value):
        self.__client_ip = value

    @property
    def transaction_id(self):
        return self.__transaction_id

    @transaction_id.setter
    def transaction_id(self, value):
        self.__transaction_id = value

    @property
    def message_type(self):
        return self.__message_type

    @message_type.setter
    def message_type(self, value):
        self.__message_type = value

    @property
    def server_ip(self):
        return self.__server_ip

    @server_ip.setter
    def server_ip(self, value):
        self.__server_ip = value

    @property
    def server_mac(self):
        return self.__server_mac

    @server_mac.setter
    def server_mac(self, value):
        self.__server_mac = value

    @property
    def lease_time(self):
        return self.__lease_time

    @lease_time.setter
    def lease_time(self, value):
        self.__lease_time = value

    @property
    def hostname(self):
        return self.__hostname

    @hostname.setter
    def hostname(self, value):
        self.__hostname = value

    @property
    def name_server(self):
        return self.__name_server

    @name_server.setter
    def name_server(self, value):
        self.__name_server = value

    @property
    def subnet_mask(self):
        return self.__subnet_mask

    @subnet_mask.setter
    def subnet_mask(self, value):
        self.__subnet_mask = value

    @property
    def router(self):
        return self.__router

    @router.setter
    def router(self, value):
        self.__router = value

    @property
    def domain(self):
        return self.__domain

    @domain.setter
    def domain(self, value):
        self.__domain = value


class DhcpClient:
    def __init__(self, client_mac_address, transaction_id):
        self.current_event = DhcpEvent.NULL
        self.current_state = DhcpState.INIT
        self.hostname = "LeeSen"
        self.mac = client_mac_address
        self.transaction_id = transaction_id
        self.ip = '0.0.0.0'
        self.server_mac = 'ff:ff:ff:ff:ff:ff'
        self.serve_ip = '255.255.255.255'
        self.request_ip = "0.0.0.0"
        self.dhcp_server_id = "0.0.0.0"
        self.default_time = 10
        self.time_1 = 0
        self.time_2 = 0
        self.time_3 = 0
        self.expired = 0
        self.arp_time = 5
        self.start_time = 0
        self.discover_stage = True

    def reset_dhcp_parameters(self):
        self.ip = '0.0.0.0'
        self.server_mac = 'ff:ff:ff:ff:ff:ff'
        self.serve_ip = '255.255.255.255'
        self.request_ip = "0.0.0.0"
        self.dhcp_server_id = "0.0.0.0"
        self.default_time = 10
        self.time_1 = 0
        self.time_2 = 0
        self.time_3 = 0
        self.expired = 0
        self.arp_time = 5
        self.start_time = 0
        self.discover_stage = True

    def reset_transaction_id(self, dhcp_transaction_id_set):
        while True:
            transaction_id_temp = randint(268435456, 4294967295)
            if transaction_id_temp not in dhcp_transaction_id_set:
                self.transaction_id = transaction_id_temp
                dhcp_transaction_id_set.add(transaction_id_temp)
                break


def init_dhcp_clients(client_number):
    mac_address_prefix = "80"

    dhcp_transaction_id_set = set()

    host_mac_address_set = set()

    """
        key: host_mac
        value: DhcpClient()
    """
    dhcp_transaction_id_dict = {}
    """
        key: host_ip
        value: host_mac
    """
    host_mac_to_ip_dict = {}
    host_mac_to_timer_dict = {}

    for i in range(0, client_number):
        transaction_id_temp = randint(268435456, 4294967295)
        if transaction_id_temp in dhcp_transaction_id_set:
            i -= 1
            continue

        mac_address_temp = mac_address_prefix + ":" + int_to_mac_suffix(transaction_id_temp)

        host_mac_address_set.add(mac_address_temp)

        dhcp_transaction_id_set.add(transaction_id_temp)

        dhcp_client_temp = DhcpClient(mac_address_temp, transaction_id_temp)

        dhcp_transaction_id_dict[mac_address_temp] = dhcp_client_temp

        host_mac_to_timer_dict[mac_address_temp] = None

        host_mac_to_ip_dict[mac_address_temp] = None

    init_data = {"dhcp_transaction_id_set": dhcp_transaction_id_set,
                 "host_mac_address_set": host_mac_address_set,
                 "dhcp_client_dict": dhcp_transaction_id_dict,
                 "host_mac_to_ip_dict": host_mac_to_ip_dict,
                 "host_mac_to_timer_dict": host_mac_to_timer_dict}
    return init_data

